-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

-- Description: 升级信号监听及回调处理
local skynet = require 'skynet'
local client = require 'power_mgmt.client'
local log = require 'mc.logging'
local c_tasks = require 'mc.orm.tasks'

local signal = {listen = {}, call_back = {}}

local function get_power_state()
    local ok, obj_list = pcall(client.GetFruCtrlObjects)
    if not ok or not next(obj_list) then
        log:debug("get fructrl object failed")
        return nil
    end
    for _, obj in pairs(obj_list) do
        return obj.PowerState
    end
end

function signal.init(db)
    signal.PowerState = get_power_state()
    signal.db =db

    client:SubscribeUpdateServiceUpdateServiceUpgradePrepareSignal(signal.upgrade_prepare_callback)
    client:SubscribeUpdateServiceUpdateServiceUpgradeProcessSignal(signal.upgrade_process_callback)
    client:SubscribeUpdateServiceUpdateServiceUpgradeFinishSignal(signal.upgrade_finish_callback)
    signal.create_fructrl_listenning()
end

function signal.upgrade_prepare_callback(ctx, system_id, firmware_type, cfg_path, file_path, para_tab)
    if signal.call_back['upgrade_prepare_callback'] then
        for _, cb in pairs(signal.call_back['upgrade_prepare_callback']) do
            c_tasks.get_instance():next_tick(cb, system_id, firmware_type, cfg_path, file_path, para_tab)
        end
    end
end

function signal.upgrade_process_callback(ctx, system_id, firmware_type, file_path, para_tab)
    if signal.call_back['upgrade_process_callback'] then
        for _, cb in pairs(signal.call_back['upgrade_process_callback']) do
            c_tasks.get_instance():next_tick(cb, signal.db, system_id, firmware_type, file_path, para_tab)
        end
    end
end

local COMP_CPLD_TYPE <const> = 'Cpld'           -- 主板CPLD
local COMP_BP_CPLD_TYPE <const> = 'BP_Cpld'     -- 背板CPLD包含内置和后置，天池除EXU外其他组件都认为是背板
function signal.upgrade_finish_callback(ctx, system_id, firmware_type, para_tab)
    local aclost_record
    if signal.db ~= nil then
        aclost_record = signal.db.AclostRecordTable({Id = 'AclostRecord'})
    end
    
    if firmware_type == COMP_CPLD_TYPE or firmware_type == COMP_BP_CPLD_TYPE then
        if aclost_record then
            aclost_record.ShieldAclostEventRecord = 1
            local ok, rsp = pcall(function ()
                aclost_record:save()
            end)
            if not ok then
                log:error('Failed to save Aclost data, err_msg: %s', rsp)
            end
        else
            log:error('get database failed.')
        end
    end
    if signal.call_back['upgrade_finish_callback'] then
        for _, cb in pairs(signal.call_back['upgrade_finish_callback']) do
            c_tasks.get_instance():next_tick(cb, system_id, firmware_type, para_tab)
        end
    end
end

local FRUCTRL_INTERFACE<const> = 'bmc.kepler.Systems.FruCtrl'

function signal.create_fructrl_listenning()
    if not signal.call_back[FRUCTRL_INTERFACE] then
        signal.call_back[FRUCTRL_INTERFACE] = {}
    end
    signal.listen[FRUCTRL_INTERFACE] = client:OnFruCtrlPropertiesChanged(function (prop_value, path, interface)
        if interface ~= FRUCTRL_INTERFACE then
            return
        end
        for _, cb in pairs(signal.call_back[FRUCTRL_INTERFACE]) do
            c_tasks.get_instance():next_tick(cb, prop_value)
        end
        if prop_value['PowerState'] then
            signal.PowerState = prop_value['PowerState']:value()
        end
    end)
end

function signal.register_callback(sig, prop_name, call_back)
    if not signal.call_back[sig] then
        signal.call_back[sig] = {}
    end
    table.insert(signal.call_back[sig], function (prop_value, ...)
        if not prop_name then
            call_back(prop_value, ...)
            return
        end
        if not prop_value[prop_name] then
            return
        end
        call_back(prop_value[prop_name]:value())
    end)
end

return signal
